using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using RedGate.SQL.Shared;
using RedGate.SQLCompare.Engine;
using RedGate.SQLDataGenerator.Engine.DataGeneration;
using RedGate.SQLDataGenerator.Engine.Generators;
using RedGate.SQLDataGenerator.Engine.Generators.Messages;
using RedGate.SQLDataGenerator.Engine.Generators.Static;
using RedGate.SQLDataGenerator.Engine.Generators.Support;
using RedGate.SQLDataGenerator.Engine.Project;

namespace CodeGenerator
{
    [Generator(typeof(object), "Generic", "Code generator", "Generates where you write your own code")]
    public class CodeGenerator : GeneratorBase, IGeneratorUIStyle, IGeneratorUI, IErrorsAndWarnings, IUniqueableGenerator
    {
        private CodeGeneratorUI m_UI;
        private string m_Source = @"using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using RedGate.SQLDataGenerator.Engine.Generators;

namespace Generators
{
    public class Generator : IGenerator
    {
        public IEnumerator GetEnumerator(GenerationSession session)
        {
            for(int i = 0; i < 100; i++)
            {
                yield return i;
            }
        }
    }
}
";
        private IGenerator m_CompiledGenerator;
        private readonly List<GeneratorMessage> m_GeneratorMessages = new List<GeneratorMessage>();
        
        public string Source
        {
            get { return m_Source; }
            set
            {
                if (m_Source != value)
                {
                    m_CompiledGenerator = null;
                }
                m_Source = value;
            }
        }     

        public UIGeneratorStyle UIGeneratorStyle
        {
            get { return UIGeneratorStyle.UIPartial; }
        }


        public IEnumerable<GeneratorMessage> GetMessages()
        {
            foreach (GeneratorMessage message in m_GeneratorMessages)
            {
                yield return message;
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool Unique
        {
            get { return true; }
            set
            {
            }
        }

        #region IGeneratorUI Members
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public UserControl UserControl
        {
            get
            {
                if (m_UI == null)
                    m_UI = new CodeGeneratorUI(this, m_Parameters);
                return m_UI;
            }
            set
            {
                m_UI = null;
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IGeneratorSimpleUI IGeneratorSimpleUI
        {
            get { return UserControl as IGeneratorSimpleUI; }
            set { m_UI = null; }
        }
        #endregion

        public CodeGenerator(GeneratorParameters parameters) : base(parameters)
        {
        }



        public void Compile()
        {
            m_CompiledGenerator = null;
            m_GeneratorMessages.Clear();

            CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("c#");

            Assembly sqlCompareAssembly = Assembly.GetAssembly(typeof(Database));
            Assembly sqlDataGeneratorAssembly = Assembly.GetAssembly(typeof(SDGProject));
            Assembly sqlSharedAssembly = Assembly.GetAssembly(typeof(Batch));

            string assemblyFileName = Path.GetTempFileName();
            CompilerParameters cp = new CompilerParameters();
            cp.GenerateInMemory = true;
            cp.GenerateExecutable = false;
            cp.OutputAssembly = assemblyFileName;
            cp.IncludeDebugInformation = false;
            cp.ReferencedAssemblies.Add(sqlDataGeneratorAssembly.Location);
            cp.ReferencedAssemblies.Add(sqlCompareAssembly.Location);
            cp.ReferencedAssemblies.Add(sqlSharedAssembly.Location);
            cp.TempFiles = new TempFileCollection(Path.GetTempPath(), false);

            Type generatorType = null;
            try
            {
                CompilerResults results = codeDomProvider.CompileAssemblyFromSource(cp, m_Source);

                if (results.Errors.Count > 0)
                {
                    foreach (CompilerError CompErr in results.Errors)
                    {
                        GeneratorMessage gm = new Error("Error compiling generator code", 
                                                        "Line number " + CompErr.Line + ", Error Number: " + CompErr.ErrorNumber +
                                                        ", '" + CompErr.ErrorText);

                        m_GeneratorMessages.Add(gm);
                    }

                    return;
                }

                Assembly a = results.CompiledAssembly;

                foreach (Type type in a.GetTypes())
                {
                    if(typeof(IGenerator).IsAssignableFrom(type))
                    {
                        generatorType = type;
                        break;
                    }
                }
            }
            catch(Exception e)
            {
                m_GeneratorMessages.Add(new Error("Error compiling generator code", e.Message));
            }
            finally
            {
                try
                {
                    if (File.Exists(assemblyFileName))
                    {
                        File.Delete(assemblyFileName);
                    }
                }
                catch
                {
                    
                }
            }

            if (generatorType != null)
            {
                ConstructorInfo cInfo = generatorType.GetConstructor(new Type[] { typeof(GeneratorParameters) });

                if (cInfo == null)
                {
                    cInfo = generatorType.GetConstructor(new Type[] { });

                    if (cInfo != null)
                    {
                        m_CompiledGenerator = (IGenerator) cInfo.Invoke(new object[] { });
                    }
                }
                else
                {
                    m_CompiledGenerator = (IGenerator)cInfo.Invoke(new object[] { m_Parameters });
                }                
            }
        }

        public override IEnumerator GetNonNullEnumerator(GenerationSession session)
        {
            if (m_CompiledGenerator == null)
            {
                if(session.ConnectionProperties == null)
                {
                    throw new NotAvailableException("This generator is not available at this time", m_Parameters.Field.Name);
                }
                else
                {
                    Compile();
                }
            }

            if (m_CompiledGenerator != null)
            {
                IEnumerator enumerator = m_CompiledGenerator.GetEnumerator(session);

                while(enumerator.MoveNext())
                {
                    yield return enumerator.Current;
                }
            }
            else
            {
                yield break;
            }
        }
    }
}